Update schema to support oneOf, and normalize objects#72
Update schema to support oneOf, and normalize objects#72k8s-ci-robot merged 3 commits intokubernetes-sigs:masterfrom
Conversation
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: apelisse The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/assign @lavalamp @kwiesmueller @jennybuckley |
typed/union.go
Outdated
| @@ -0,0 +1,130 @@ | |||
| /* | |||
| Copyright 2018 The Kubernetes Authors. | |||
typed/union_test.go
Outdated
| @@ -0,0 +1,224 @@ | |||
| /* | |||
| Copyright 2018 The Kubernetes Authors. | |||
typed/union.go
Outdated
| return set | ||
| } | ||
|
|
||
| // Set the discriminator to a specific value |
typed/union.go
Outdated
| if union.Discriminator != nil { | ||
| df = uncapitalize(*union.Discriminator) | ||
| odv := getDiscriminatorValue(old, df) | ||
| ndv := getDiscriminatorValue(new, df) |
There was a problem hiding this comment.
Do we also need to uncapitalize the odv and ndv? It seems a bit kubernetes specific.
typed/union_test.go
Outdated
| unions: | ||
| - discriminator: discriminator | ||
| fields: [a, b, c] | ||
| - fields: [one, two, three]`) |
There was a problem hiding this comment.
Test looks good, maybe we should also try some nested structs
There was a problem hiding this comment.
Also we should test that creating a new object should still set the discriminator correctly
typed/union.go
Outdated
|
|
||
| for field := range ofs { | ||
| if _, ok := nfs[field]; !ok { | ||
| out.MapValue.Delete(field) |
There was a problem hiding this comment.
Maybe we could use the same
for _, field := range union.Fields {
if field != ndv {
out.MapValue.Delete(field)
}
}
loop from before (or a function that does that) and not worry about ofs or nfs anymore?
typed/union.go
Outdated
| } | ||
|
|
||
| if df != "" { | ||
| out.MapValue.Set(df, value.StringValue(dv)) |
There was a problem hiding this comment.
value.StringValue(capitalize(dv))?
schema/elements.go
Outdated
| // one or more fields in the above list. A given field from the above | ||
| // list may be referenced in exactly 0 or 1 places in the below list. | ||
| // Unions []Union `yaml:"unions,omitempty"` | ||
| Unions []Union `yaml:"unions,omitempty"` |
There was a problem hiding this comment.
One of the reasons this was commented out is that I wasn't sure if I wanted to do it the way you've added it, or if it would be better to use a struct per union and support embedding a list of structs. The other way would make it impossible to try and put the same field into multiple unions. I'm not sure where, but it'd be nice to see a discussion of why this format is chosen. (Maybe you have it and I haven't seen it yet.)
There was a problem hiding this comment.
The way it's going, we're only going to accept one union per structure in the OpenAPI, which would prevent any sort of overlap between fields
schema/elements.go
Outdated
| // one or more fields in the above list. A given field from the above | ||
| // list may be referenced in exactly 0 or 1 places in the below list. | ||
| // Unions []Union `yaml:"unions,omitempty"` | ||
| Unions []Union `yaml:"unions,omitempty"` |
There was a problem hiding this comment.
Also consider a different name, since you have to explain "Union". Maybe Exclusivities or OneOfs?
lavalamp
left a comment
There was a problem hiding this comment.
Super exciting change, I especially appreciate the thorough tests!
schema/elements.go
Outdated
| } | ||
|
|
||
| // Union, or oneof, means that only one of multiple fields of a structure can be | ||
| // set at all times. For backward compatibility reasons, and to help "dumb |
There was a problem hiding this comment.
| // set at all times. For backward compatibility reasons, and to help "dumb | |
| // set at a time. For backward compatibility reasons, and to help "dumb |
schema/elements.go
Outdated
| // set at all times. For backward compatibility reasons, and to help "dumb | ||
| // clients" that are not aware of union (or can't be aware of this because they | ||
| // don't know what fields are part of the union), the code tolerates multiple | ||
| // fields to be set but will try to detect which fields must be cleaned (there |
There was a problem hiding this comment.
| // fields to be set but will try to detect which fields must be cleaned (there | |
| // fields to be set but will try to detect which fields must be cleared (there |
schema/elements.go
Outdated
| // don't know what fields are part of the union), the code tolerates multiple | ||
| // fields to be set but will try to detect which fields must be cleaned (there | ||
| // should never be more than two though): | ||
| // - If the Discriminator field exist, the value of the field will be used to |
There was a problem hiding this comment.
| // - If the Discriminator field exist, the value of the field will be used to | |
| // - If the Discriminator field exists, the value of the field will be used to |
schema/elements.go
Outdated
|
|
||
| // Union, or oneof, means that only one of multiple fields of a structure can be | ||
| // set at all times. For backward compatibility reasons, and to help "dumb | ||
| // clients" that are not aware of union (or can't be aware of this because they |
There was a problem hiding this comment.
| // clients" that are not aware of union (or can't be aware of this because they | |
| // clients" which are not aware of the union (or can't be aware of it, because they |
schema/elements.go
Outdated
| // fields to be set but will try to detect which fields must be cleaned (there | ||
| // should never be more than two though): | ||
| // - If the Discriminator field exist, the value of the field will be used to | ||
| // decide which field to clean and which field to keep |
There was a problem hiding this comment.
| // decide which field to clean and which field to keep | |
| // decide which field to clear and which field to keep |
typed/union_test.go
Outdated
| { | ||
| name: "client sends multiple with discriminator", | ||
| old: `{}`, | ||
| new: `{"a": 1, "b": 1, "discriminator": "a"}`, |
There was a problem hiding this comment.
I also think this should cause an error.
There was a problem hiding this comment.
Actually, the use-case in mind here is the following:
- I'm using kustomize
- my base deployment uses an emptyDir
- my overlay uses a persistent disk
- kustomize typically merges this by having both (you have to explicitely set
emptyDirtonull)
In that case, you could use the discriminator to clarify (which is much better than setting discriminator to null)
There was a problem hiding this comment.
It sounds like kustomize should use this library to do the merges. It'd be pretty nifty if customize produced output which had each overlay as a separate field manager.
There was a problem hiding this comment.
So this would mean someone could send
deploymentStrategyType: Recreate
rollingUpdate:
maxUnavailable: 1
and it would be accepted and treated as this now?
deploymentStrategyType: Recreate
rollingUpdate: {}
| name: "change discriminator, nothing else", | ||
| old: `{"discriminator": "a"}`, | ||
| new: `{"discriminator": "random"}`, | ||
| out: `{"discriminator": "random"}`, |
There was a problem hiding this comment.
random discriminator values should cause an error, don't you think?
There was a problem hiding this comment.
I think this is similar to the one below, which is to make sure changing a DeploymentStrategyType from RollingUpdate to Recreate clears the RollingUpdate field.
Recreate isn't a name of a field in the Union but it is an allowed value of DeploymentStrategyType. If it's not allowed as a DeploymentStrategyType, I think validation outside smd could handle that kind of thing.
typed/union_test.go
Outdated
| name: "remove discriminator, nothing else", | ||
| old: `{"discriminator": "a", "a": 1}`, | ||
| new: `{"a": 1}`, | ||
| out: `{"a": 1}`, |
There was a problem hiding this comment.
I guess I would have expected a to be cleared also--not 100% sure.
typed/union_test.go
Outdated
| { | ||
| name: "new object has three of same union set", | ||
| old: `{"a": 1}`, | ||
| new: `{"a": 2, "b": 1, "c": 3}`, |
There was a problem hiding this comment.
This should cause an error, I think.
There was a problem hiding this comment.
I think this would cause an error outside smd as well
typed/union_test.go
Outdated
| { | ||
| name: "old object has two of same union set", | ||
| old: `{"a": 1, "b": 2}`, | ||
| new: `{"a": 2, "b": 1}`, |
There was a problem hiding this comment.
I'd like a test case exactly like this except only one of the values changes.
schema/elements.go
Outdated
| // parent structure. Discriminator (if oneOf has one), is NOT included | ||
| // in this list. The value for field is how we map the name of the field | ||
| // to actual value for discriminator. | ||
| Fields map[string]string `yaml:"fields,omitempty"` |
There was a problem hiding this comment.
can a value be mapped to from multiple keys?
There was a problem hiding this comment.
Also maybe this should be a list, since we use lists in all the other schema pieces
There was a problem hiding this comment.
Also maybe this should be a list, since we use lists in all the other schema pieces
Good point
can a value be mapped to from multiple keys?
No, it shouldn't. The part that converts the openapi into this schema is going to verify that this is not possible. If it still happens that we have two mappings going to the same key, it will probably use the last one in the list.
4f934de to
ceb39c1
Compare
schema/elements.go
Outdated
| FieldName string `yaml:"fieldName"` | ||
| // DiscriminatedBy is the value of the discriminator to select that | ||
| // field. | ||
| DiscriminatedBy string `yaml:"DiscriminatedBy"` |
There was a problem hiding this comment.
What would this field be set to in the case of Unions that don't have a discriminator?
schema/elements.go
Outdated
| Discriminator *string `yaml:"discriminator,omitempty"` | ||
|
|
||
| // This is the list of fields that belong to this union. This fields are | ||
| // required to not be part of another union, or be the discriminator for |
|
Fixed |
Normalization can return errors if something very wrong is happening, like setting two new fields, or if setting a new field while also changing the discriminator.
|
/lgtm |
|
I forgot to cc @sttts, I'll happily update code based on your comments Stefan! |
This does mostly two things:
And adds tests. This is still not plugged anyway though, so it's not going to do anything.